package com.novel.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.novel.config.RedissonHelper;
import com.novel.entiy.*;
import com.novel.entiy.dto.CommentDTO;
import com.novel.entiy.dto.CommentPageDTO;
import com.novel.entiy.event.CommentSupportEvent;
import com.novel.entiy.event.HotEvent;
import com.novel.entiy.vo.ChapterVO;
import com.novel.entiy.vo.CommentVO;
import com.novel.exception.BusinessException;
import com.novel.mapper.CommentMapper;
import com.novel.mapper.CommentSupportMapper;
import com.novel.mapper.NovelMapper;
import com.novel.service.CommentService;
import com.novel.service.UserService;
import com.novel.utils.ConverterUtil;
import com.novel.utils.PageResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.redisson.api.RLock;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author tanghao
 * @date 2022/4/8
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CommentServiceImpl implements CommentService {

    private final UserService userService;

    private final CommentMapper commentMapper;

    private final NovelMapper novelMapper;

    private final RedissonHelper redissonHelper;

    private final ApplicationEventPublisher applicationEventPublisher;

    private final CommentSupportMapper commentSupportMapper;

    @Override
    public void comment(CommentDTO commentDTO) {
        TpUser userInfo = userService.getUserInfo();
        Comment comment = commentDTO.doForward();
        comment.setCommentUserId(userInfo.getId());
        comment.setCommentUserName(userInfo.getNickname());
        comment.setCommentNums(0L);
        Novel novel = novelMapper.selectById(commentDTO.getNovelId());
        commentMapper.insert(comment);
        applicationEventPublisher.publishEvent(HotEvent.builder().novelId(commentDTO.getNovelId())
                .hotNums(2000L).build());
        commentNum(novel, 1L);
    }

    public void commentNum(Novel novel, Long num) {
        RLock lock = redissonHelper.getLock("comment-num" + novel.getId());
        try {
            if (lock.tryLock(100, 10000, TimeUnit.MILLISECONDS)) {
                try {
                    novel.setCommentNums(novel.getCommentNums() + num);
                    novelMapper.updateById(novel);
                } finally {
                    lock.unlock();
                }
            } else {
                commentNum(novel, num);
            }
        } catch (InterruptedException e) {
            log.error("增加评论数操作失败 novelId:{}", novel.getId(), e);
        }
    }


    @Override
    public PageResult<CommentVO> getCommentList(CommentPageDTO commentPageDTO) {
        TpUser userInfo;
        try {
            userInfo = userService.getUserInfo();
        } catch (BusinessException e) {
            userInfo = null;
        }
        Map<Long, List<CommentSupport>> commentMap = null;
        if (ObjectUtils.isNotEmpty(userInfo)) {
            //获取当前用户是否点赞评论
            List<CommentSupport> commentSupportList = commentSupportMapper.selectList(new QueryWrapper<CommentSupport>()
                    .eq(CommentSupport.COL_NOVEL_ID, commentPageDTO.getNovelId())
                    .eq(CommentSupport.COL_USER_ID, userInfo.getId()));
            commentMap = commentSupportList.stream()
                    .collect(Collectors.groupingBy(CommentSupport::getCommentId));
        }
        Page<Comment> commentPage = commentMapper.selectPage(new Page<>(commentPageDTO.getPageNum(), commentPageDTO.getPageSize(), true),
                new QueryWrapper<Comment>().eq(Comment.COL_NOVEL_ID, commentPageDTO.getNovelId())
                        .eq(Comment.COL_COMMENT_PARENT_ID, 0)
                        .orderByDesc(Comment.COL_CREATED_TIME));
        List<Comment> commentList = commentPage.getRecords();
        //获取当前评论的直接回复
        Map<Long, List<Comment>> replyMap = commentMapper.selectList(new QueryWrapper<Comment>().eq(Comment.COL_NOVEL_ID, commentPageDTO.getNovelId())
                .ne(Comment.COL_COMMENT_PARENT_ID, 0)
                .last("limit 2")
                .orderByDesc(Comment.COL_CREATED_TIME)).stream().collect(Collectors.groupingBy(Comment::getCommentParentId));
        TpUser finalUserInfo = userInfo;
        Map<Long, List<CommentSupport>> finalCommentMap = commentMap;
        List<CommentVO> commentVOList = commentList.parallelStream().map(comment -> {
            List<Comment> reply = replyMap.get(comment.getId());
            List<CommentVO> replyVOList = ConverterUtil.convertList(reply);
            if (CollectionUtils.isNotEmpty(replyVOList)) {
                replyVOList.forEach(replyVO -> {
                    if (ObjectUtils.isNotEmpty(finalUserInfo)) {
                        if (CollectionUtils.isNotEmpty(finalCommentMap.get(replyVO.getId()))) {
                            replyVO.setAlreadySupport(Boolean.TRUE);
                        } else {
                            replyVO.setAlreadySupport(Boolean.FALSE);
                        }
                    }
                });
            }
            CommentVO commentVO = comment.doForward();
            commentVO.setCommentReplyList(replyVOList);
            if (ObjectUtils.isNotEmpty(finalUserInfo)) {
                if (CollectionUtils.isNotEmpty(finalCommentMap.get(comment.getId()))) {
                    commentVO.setAlreadySupport(Boolean.TRUE);
                } else {
                    commentVO.setAlreadySupport(Boolean.FALSE);
                }
            }
            return commentVO;
        }).collect(Collectors.toList());
        return new PageResult<>(
                commentVOList, (int) commentPage.getTotal(), (int) commentPage.getSize(), (int) commentPage.getCurrent());
    }

    @Override
    public CommentVO getAllReply(Long commentId) {
        Comment comment = commentMapper.selectById(commentId);
        CommentVO commentVO = comment.doForward();
        List<Comment> comments = commentMapper.selectList(new QueryWrapper<Comment>().eq(Comment.COL_COMMENT_PARENT_ID, commentId));
        List<CommentVO> commentVOList = ConverterUtil.convertList(comments);
        commentVO.setCommentReplyList(commentVOList);
        return commentVO;
    }

    @Override
    public void commentSupport(Long commentId, Long novelId, Integer type) {
        TpUser userInfo = userService.getUserInfo();
        Long userId = userInfo.getId();
        CommentSupport commentSupport = commentSupportMapper.selectOne(new QueryWrapper<CommentSupport>()
                .eq(CommentSupport.COL_COMMENT_ID, commentId).eq(CommentSupport.COL_USER_ID, userId));
        if (ObjectUtils.isNotEmpty(commentSupport) && type.equals(0)) {
            throw new BusinessException("你已经点过赞了");
        }
        if (ObjectUtils.isEmpty(commentSupport) && type.equals(1)) {
            throw new BusinessException("操作失败请重试");
        }
        String key = "support-sum-" + commentId + "-" + userId;
        long count;
        if (ObjectUtils.isNotEmpty(redissonHelper.getValue(key))) {
            if (type.equals(0)) {
                count = redissonHelper.getIncrement(key, 7L);
            } else {
                count = redissonHelper.getDecrement(key, 7L);
            }
            //发送事件
            applicationEventPublisher.publishEvent(CommentSupportEvent.builder()
                    .commentSupportNum(count)
                    .userId(userId)
                    .commentId(commentId)
                    .novelId(novelId)
                    .type(type)
                    .build());
        } else {
            getSupportSum(commentId, userId);
            commentSupport(commentId, novelId, type);
        }
    }

    /**
     * 获取点赞数
     *
     * @param commentId 评论id
     * @param userId    用户id
     */
    public void getSupportSum(Long commentId, Long userId) {
        String key = "support-sum-" + commentId + "-" + userId;
        String lockKey = "support-lock-" + commentId + "-" + userId;
        RLock lock = redissonHelper.getLock(lockKey);
        try {
            if (lock.tryLock(100, 10000, TimeUnit.MILLISECONDS)) {
                try {
                    if (ObjectUtils.isEmpty(redissonHelper.getValue(key))) {
                        Comment comment = commentMapper.selectById(commentId);
                        redissonHelper.addAndGet(key, comment.getCommentNums(), 7L);
                    }
                } finally {
                    lock.unlock();
                }
            } else {
                getSupportSum(commentId, userId);
            }
        } catch (InterruptedException e) {
            log.error("获取点赞数失败 commentId:{}", commentId, e);
        }
    }


    /**
     * 查询自己书籍下的所有评论
     *
     * @param commentPageDTO 参数
     * @return 结果
     */
    @Override
    public PageResult<CommentVO> getNovelCommentAll(CommentPageDTO commentPageDTO) {
        Page<Comment> commentPage = commentMapper.selectPage(new Page<>(commentPageDTO.getPageNum(), commentPageDTO.getPageSize(), true),
                new QueryWrapper<Comment>().eq(Comment.COL_NOVEL_ID, commentPageDTO.getNovelId())
                        .orderByDesc(Comment.COL_CREATED_TIME));
        List<Comment> pageRecords = commentPage.getRecords();
        List<CommentVO> commentVOList = ConverterUtil.convertList(pageRecords);
        return new PageResult<>(commentVOList, (int) commentPage.getTotal(), (int) commentPage.getSize(), (int) commentPage.getCurrent());
    }

    /**
     * 删除自己书籍的评论
     *
     * @param commentId 评论id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delComment(Long commentId) {
        commentMapper.deleteById(commentId);
        //删除评论点赞记录
        commentSupportMapper.delete(new QueryWrapper<CommentSupport>().eq(CommentSupport.COL_COMMENT_ID, commentId));
        Comment comment = commentMapper.selectById(commentId);
        Long novelId = comment.getNovelId();
        Novel novel = novelMapper.selectById(novelId);
        commentNum(novel, -1L);
    }
}
